﻿using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;

namespace Hont.HyperWigglePackage.Editor
{
    public partial class HyperWiggleEditorView
    {
        const float PREVIEW_HEIGHT = 260;

        bool mPreviewOperating;
        bool mIsEditFPS;
        Vector3 mLerpCameraBeginPosition;
        Vector3 mCameraBeginPosition;

        Quaternion mLerpCameraBeginRotation;
        Quaternion mCameraBeginRotation;

        PreviewVO mPreviewVO;
        PreviewRenderUtility mPreviewRenderUtility;

        public PreviewRenderUtility PreviewRenderUtility { get { return mPreviewRenderUtility; } }


        void InitPreviewPartial()
        {
            mPreviewVO = new PreviewVO();

            ResetCameraBeginInfo();
        }

        public void UpdatePreview(PreviewVO previewVO)
        {
            mPreviewVO = previewVO;
        }

        public void SetCameraBeginInfo(Vector3 position, Quaternion rotation)
        {
            mCameraBeginPosition = position;
            mLerpCameraBeginPosition = mCameraBeginPosition;

            mCameraBeginRotation = rotation;
            mLerpCameraBeginRotation = mCameraBeginRotation;
        }

        public void GetCameraBeginInfo(out Vector3 position, out Quaternion rotation)
        {
            position = mCameraBeginPosition;
            rotation = mCameraBeginRotation;
        }

        public void ResetCameraBeginInfo()
        {
            mCameraBeginPosition = new Vector3(0, 0, -10);
            mLerpCameraBeginPosition = mCameraBeginPosition;

            mCameraBeginRotation = Quaternion.LookRotation(Vector3.forward);
            mLerpCameraBeginRotation = mCameraBeginRotation;
        }

        void RefreshPreview()
        {
            GUI.Box(new Rect(0, 0, Screen.width, PREVIEW_HEIGHT), "");

            RefreshPlayButtons();
            RefreshXYZText();
            RefreshImage();
            RefreshTextTips();
        }

        void RefreshTextTips()
        {
            var mid = Screen.width * 0.5f;
            var width = 26;
            var rect = new Rect(mid - width, PREVIEW_HEIGHT - 93, width * 2, 20);

            if (!mIsEditFPS && rect.Contains(mMousePosition) && mIsMouseUpOnce)
            {
                mIsEditFPS = true;
            }

            if (mIsEditFPS)
            {
                GUI.Label(rect, "fps: ");

                rect.height = 18;

                rect.x += 30;
                var fps = EditorGUI.FloatField(rect, mPreviewVO.FPS);

                if (mPreviewVO.FPS != fps)
                {
                    if (mPreviewVO.OnChangedFPSCB != null)
                        mPreviewVO.OnChangedFPSCB(fps);
                }

                rect.x += 50;
                rect.size = new Vector2(45, rect.height);
                if (GUI.Button(rect, "apply"))
                {
                    GUI.FocusControl("");
                    mIsEditFPS = false;
                }
            }
            else
            {
                GUI.Label(rect, "fps: " + mPreviewVO.FPS);
            }
        }

        void RefreshImage()
        {
            var edgeRect = new Rect(0, 0, Screen.width, PREVIEW_HEIGHT);
            var modify = edgeRect.size * 0.25f;
            edgeRect.x += modify.x;
            edgeRect.y += modify.y;
            edgeRect.width -= modify.x * 2;
            edgeRect.height -= modify.y * 2;
            edgeRect.y -= 30;

            GUI.Box(new Rect(edgeRect.x - SMALL_SPACING, edgeRect.y - SMALL_SPACING, edgeRect.width + SMALL_SPACING * 2, edgeRect.height + SMALL_SPACING * 2), "");

            if (mPreviewRenderUtility == null)
            {
                mPreviewRenderUtility = new PreviewRenderUtility();
                mPreviewRenderUtility.camera.farClipPlane = 500;
                mPreviewRenderUtility.camera.nearClipPlane = 0.003f;
                mPreviewRenderUtility.camera.clearFlags = CameraClearFlags.SolidColor;
            }

            if (edgeRect.Contains(mMousePosition) && mLastButton == 1 && mIsMouseUpOnce && mMouseDownPosition == mMousePosition)
            {
                var genericMenu = new GenericMenu();
                for (int i = 0; i < mPreviewVO.OtherModelsArray.Length; i++)
                {
                    var item = mPreviewVO.OtherModelsArray[i];
                    genericMenu.AddItem(new GUIContent(string.Format("Change Model To '{0}'", item))
                        , false
                        , () =>
                        {
                            if (mPreviewVO.OnChangePreviewModelCB != null)
                                mPreviewVO.OnChangePreviewModelCB(item);
                        });
                }

                genericMenu.AddSeparator("");

                for (int i = 0; i < mPreviewVO.OtherSkyboxsArray.Length; i++)
                {
                    var item = mPreviewVO.OtherSkyboxsArray[i];

                    genericMenu.AddItem(new GUIContent(string.Format("Change Cubemap To '{0}'", item))
                        , false
                        , () =>
                        {
                            if (mPreviewVO.OnChangeCubemapCB != null)
                                mPreviewVO.OnChangeCubemapCB(item);
                        });
                }

                genericMenu.ShowAsContext();
            }

            #region --- Camera Operation ---
            var mouseDelta = mMousePosition - mLastMousePosition;
            var dir = mLerpCameraBeginPosition - Vector3.zero;
            var camera = mPreviewRenderUtility.camera;

            if (edgeRect.Contains(mMousePosition) && mIsMouseDown)
                mPreviewOperating = true;

            if (mPreviewOperating && mIsMouseUpOnce)
                mPreviewOperating = false;

            dir += dir.normalized * -mMouseWheelDelta.y * 0.1f;
            mLerpCameraBeginPosition = dir;

            if (mPreviewOperating && mIsMouseMidDown)
            {
                mLerpCameraBeginPosition += -mouseDelta.x * 0.03f * camera.transform.right;
                mLerpCameraBeginPosition += mouseDelta.y * 0.04f * camera.transform.up;
            }

            else if (mPreviewOperating && mIsMouseRightDown)
            {
                var head = Quaternion.AngleAxis(mouseDelta.x * 0.5f, camera.transform.up);
                var pitch = Quaternion.AngleAxis(mouseDelta.y * 0.5f, camera.transform.right);

                var newDir = head * pitch * (-dir.normalized * 8);

                mLerpCameraBeginPosition = Vector3.zero - newDir;
                mLerpCameraBeginRotation = Quaternion.LookRotation(newDir);
            }

            else if (mPreviewOperating && mIsMouseDown && mIsHoldAlt)
            {
                var head = Quaternion.AngleAxis(mouseDelta.x * 0.1f, camera.transform.up);
                var pitch = Quaternion.AngleAxis(mouseDelta.y * 0.1f, camera.transform.right);

                mLerpCameraBeginRotation = head * pitch * mLerpCameraBeginRotation;
            }

            mCameraBeginPosition = Vector3.Lerp(mCameraBeginPosition, mLerpCameraBeginPosition, 0.2f);
            mCameraBeginRotation = Quaternion.Lerp(mCameraBeginRotation, mLerpCameraBeginRotation, 0.2f);

            if (mPreviewVO.OnPreviewCameraTransformUpdateCB != null)
                mPreviewVO.OnPreviewCameraTransformUpdateCB(camera, mCameraBeginPosition, mCameraBeginRotation);

            #endregion

            GUI.BeginClip(edgeRect);

            var drawRect = new Rect(0, 0, edgeRect.width, edgeRect.height);
            mPreviewRenderUtility.BeginPreview(drawRect, GUIStyle.none);

            InternalEditorUtility.SetCustomLighting(mPreviewRenderUtility.lights, new Color(0.6f, 0.6f, 0.6f, 1f));

            mPreviewRenderUtility.DrawMesh(mPreviewVO.PreviewModel, Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(30, 45, 0), Vector3.one), EditorAssetsCache.PreviewMaterial, 0);
            mPreviewRenderUtility.DrawMesh(EditorAssetsCache.CubemapMesh, Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(0, 0, 0), Vector3.one * 100), mPreviewVO.CubemapMaterial, 0);

            if (mPreviewVO.OnPreviewCameraWiggleUpdateCB != null)
                mPreviewVO.OnPreviewCameraWiggleUpdateCB(mPreviewRenderUtility.camera);

            mPreviewRenderUtility.camera.Render();
            var texture = mPreviewRenderUtility.EndPreview();

            InternalEditorUtility.RemoveCustomLighting();

            GUI.Box(drawRect, texture, GUIStyle.none);

            GUI.EndClip();

            if (mPreviewVO.IsInvalidWiggle)
            {
                var invalidTipRect = new Rect(edgeRect.center.x - 60, edgeRect.y - 10, 160, 60);
                GUI.Box(invalidTipRect, EditorAssetsCache.InvalidWiggleTex, GUIStyle.none);
                invalidTipRect.x -= 10;
                invalidTipRect.y -= 23;
                invalidTipRect.width += 70;
                GUI.Label(invalidTipRect
                    , string.Format("P: {0}\nR:{1}", mPreviewVO.InvalidPositon.ToString("f3"), mPreviewVO.InvalidRotation.ToString("f3")));
            }
        }

        void RefreshPlayButtons()
        {
            var mid = Screen.width * 0.5f;
            GUILayout.BeginArea(new Rect(mid - 100, PREVIEW_HEIGHT - 75, 200, 40));
            GUILayout.BeginHorizontal();

            if (GUILayout.Button("|<", GUILayout.Height(30)))
            {
                GUI.FocusControl("");

                if (mPreviewVO.OnToBeginFrameBtnClickedCB != null)
                    mPreviewVO.OnToBeginFrameBtnClickedCB();
            }
            if (GUILayout.Button("<", GUILayout.Height(30)))
            {
                GUI.FocusControl("");

                if (mPreviewVO.OnToPreviousFrameBtnClickedCB != null)
                    mPreviewVO.OnToPreviousFrameBtnClickedCB();
            }
            if (GUILayout.Button(mPreviewVO.IsPlaying ? "||" : "GO", GUILayout.Height(30)))
            {
                GUI.FocusControl("");

                var isPlaying = !mPreviewVO.IsPlaying;

                if (mPreviewVO.OnPlayBtnClickedCB != null)
                    mPreviewVO.OnPlayBtnClickedCB(isPlaying);
            }
            if (GUILayout.Button(">", GUILayout.Height(30)))
            {
                GUI.FocusControl("");

                if (mPreviewVO.OnToNextFrameBtnClickedCB != null)
                    mPreviewVO.OnToNextFrameBtnClickedCB();
            }
            if (GUILayout.Button(">|", GUILayout.Height(30)))
            {
                GUI.FocusControl("");

                if (mPreviewVO.OnToEndFrameBtnClickedCB != null)
                    mPreviewVO.OnToEndFrameBtnClickedCB();
            }

            GUILayout.EndHorizontal();
            GUILayout.EndArea();
        }

        void RefreshXYZText()
        {
            var px = mTimelineVO.TracksArray[TrackConst.PX].Value[mTimelineVO.CurrentFrame];
            var py = mTimelineVO.TracksArray[TrackConst.PY].Value[mTimelineVO.CurrentFrame];
            var pz = mTimelineVO.TracksArray[TrackConst.PZ].Value[mTimelineVO.CurrentFrame];

            var rx = mTimelineVO.TracksArray[TrackConst.RX].Value[mTimelineVO.CurrentFrame];
            var ry = mTimelineVO.TracksArray[TrackConst.RY].Value[mTimelineVO.CurrentFrame];
            var rz = mTimelineVO.TracksArray[TrackConst.RZ].Value[mTimelineVO.CurrentFrame];

            GUILayout.BeginArea(new Rect(LARGE_SPACING, PREVIEW_HEIGHT - 40, Screen.width - LARGE_SPACING * 2, 40));
            GUILayout.BeginHorizontal();

            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField("px", GUILayout.Width(30));
            var returnPX = EditorGUILayout.FloatField(px, GUILayout.Height(15), GUILayout.Width(60));
            EditorGUILayout.EndHorizontal();

            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField("py", GUILayout.Width(30));
            var returnPY = EditorGUILayout.FloatField(py, GUILayout.Height(15), GUILayout.Width(60));
            EditorGUILayout.EndHorizontal();

            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField("pz", GUILayout.Width(30));
            var returnPZ = EditorGUILayout.FloatField(pz, GUILayout.Height(15), GUILayout.Width(60));
            EditorGUILayout.EndHorizontal();

            GUILayout.EndHorizontal();

            GUILayout.BeginHorizontal();

            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField("rx", GUILayout.Width(30));
            var returnRX = EditorGUILayout.FloatField(rx, GUILayout.Height(15), GUILayout.Width(60));
            EditorGUILayout.EndHorizontal();

            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField("ry", GUILayout.Width(30));
            var returnRY = EditorGUILayout.FloatField(ry, GUILayout.Height(15), GUILayout.Width(60));
            EditorGUILayout.EndHorizontal();

            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField("rz", GUILayout.Width(30));
            var returnRZ = EditorGUILayout.FloatField(rz, GUILayout.Height(15), GUILayout.Width(60));
            EditorGUILayout.EndHorizontal();

            GUILayout.EndHorizontal();
            GUILayout.EndArea();

            if (returnPX != px)
                mPreviewVO.OnChangedValueCB(TrackConst.PX, returnPX);
            if (returnPY != py)
                mPreviewVO.OnChangedValueCB(TrackConst.PY, returnPY);
            if (returnPZ != pz)
                mPreviewVO.OnChangedValueCB(TrackConst.PZ, returnPZ);

            if (returnRX != rx)
                mPreviewVO.OnChangedValueCB(TrackConst.RX, returnRX);
            if (returnRY != ry)
                mPreviewVO.OnChangedValueCB(TrackConst.RY, returnRY);
            if (returnRZ != rz)
                mPreviewVO.OnChangedValueCB(TrackConst.RZ, returnRZ);
        }
    }
}
